package com.game.autoTool.project.web.operate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.game.autoTool.project.web.common.exception.CustomException;
import com.game.autoTool.project.web.domain.req.GetFunctionFlowReq;
import com.game.autoTool.project.web.domain.resp.GameFunctionOperateFlowResp;
import com.game.autoTool.project.web.domain.resp.ScreenshotOcrResp;
import com.game.autoTool.project.web.utils.HttpClientUtil;
import com.game.autoTool.project.web.utils.RandomNumberBetweenTwoParams;

import java.awt.*;
import java.awt.event.InputEvent;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 自动执行脚本
 * 1.先通过http获取对应功能的集合列
 * 2.一步一步的进行模拟操作
 */
public class AutoExecutionTool {

    public static void main(String[] args) {
        GetFunctionFlowReq req = new GetFunctionFlowReq();
        req.setFunctionItemsId(1213246765L);

        String res = HttpClientUtil.sendPost("http://127.0.0.1:28888/game/function/flow", JSONObject.parseObject(JSONObject.toJSONString(req)));

        System.out.println(res);

        JSONObject jsonObject = JSONObject.parseObject(res);
        int code = jsonObject.getIntValue("code");

        if (code != 200) {
            String msg = jsonObject.getString("msg");
            System.out.println("请求失败，错误码: " + code + "，错误信息: " + msg);
            throw new CustomException(msg);
        }

        // 假设 200 表示请求成功
        Object data = jsonObject.get("data");
        System.out.println("请求成功，提取的 data 字段值: " + data);

        List<GameFunctionOperateFlowResp> dataList = jsonObject.getObject("data", new TypeReference<List<GameFunctionOperateFlowResp>>() {
        });

        if (CollectionUtil.isEmpty(dataList)) {
            System.out.println("data 字段为空");
            throw new CustomException("未找到对应流程");
        }

        Map<Long, GameFunctionOperateFlowResp> respMap = dataList.stream().collect(Collectors.toMap(GameFunctionOperateFlowResp::getId, Function.identity()));


        //需要记录此时鼠标的位置,等下一条数据的时候,模拟用户的操作去移动鼠标的位置


        try {
            // 创建 Robot 对象
            Robot robot = new Robot();

            // 延迟 2 秒，以便有时间切换到目标窗口
            robot.delay(1000);

            // 直接取第一个
            GameFunctionOperateFlowResp currentResp = dataList.get(0);

            //拖动时放入第一条记录
            GameFunctionOperateFlowResp startDragResp = null;

            //用于记录当前鼠标的位置。该变量的类型为 java.awt.Point，它包含 x 和 y 坐标。
            Point currentMousePos = null;

            while (ObjectUtil.isNotEmpty(currentResp) && currentResp != null) {
                System.out.println(currentResp);
                Long nextId = currentResp.getNextId();
                if (nextId == null) {
                    break;
                }

                Integer targetX = currentResp.getTargetX();
                Integer targetY = currentResp.getTargetY();
                int operateType = currentResp.getOperateType();
                Integer stayTime = currentResp.getStayTime();
                Integer isNeedScreenshot = currentResp.getIsNeedScreenshot();
                Integer keyInputCode = currentResp.getKeyInputCode();
                ScreenshotOcrResp screenshotOcrResp = currentResp.getScreenshotOcrResp();

                if (stayTime > 0) {
                    robot.delay(stayTime);
                }

                // 记录当前鼠标位置
                currentMousePos = MouseInfo.getPointerInfo().getLocation();

                // 操作类型(11键盘按下,12键盘释放,21鼠标移动,22鼠标拖动,23鼠标左键点击,24鼠标右键点击,25鼠标向上滚动,26鼠标向下滚动)
                if (operateType == 11) { // 11键盘按下
                    // 操作键盘 按下时是一条数据,释放时是一条数据,组合键也是同样的
                    // 释放键盘上的键对应的code
                    if (keyInputCode != null) {
                        robot.keyPress(keyInputCode);
                    }
                } else if (operateType == 12) { // 12键盘释放
                    // 释放键盘上的键对应的code
                    if (keyInputCode != null) {
                        robot.keyRelease(keyInputCode);
                    }
                } else if (operateType == 21) { // 21鼠标移动
                    if (targetX != null && targetY != null) {
                        //模拟移动
                        simulateMouseMove(robot, currentMousePos, targetX, targetY);
                    }
                } else if (operateType == 22) {
                    // 拖动时,第一条数据是开始的x,y坐标,循环的下一条是结束的x,y坐标,需要先记录第一条拖动的记录,等第二条来到之后,在统一执行
                    if (startDragResp == null) {
                        // 记录第一条拖动记录
                        startDragResp = currentResp;
                    } else {
                        // 第二条记录到来，执行拖动操作
                        //第一条记录的位置
                        Integer startX = startDragResp.getTargetX();
                        Integer startY = startDragResp.getTargetY();
                        //第二条记录的位置
                        Integer endX = targetX;
                        Integer endY = targetY;

                        if (startX != null && startY != null) {
                            //1. 模拟鼠标移动,从当前位置移动到拖动开始位置
                            simulateMouseMove(robot, currentMousePos, startX, startY);

                            robot.delay(RandomNumberBetweenTwoParams.generateRandomNumber(0,2));

                            // 2. 按下鼠标左键（开始拖动）
                            robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                             // 短暂停顿，确保按下生效
                            robot.delay(RandomNumberBetweenTwoParams.generateRandomNumber(0,2));

                            //更新鼠标当前的位置
                            currentMousePos = MouseInfo.getPointerInfo().getLocation();
                            //3. 移动到第二条记录的位置,计算他需要移动的步数,每一个坐标点算一步
                            simulateMouseMove(robot, currentMousePos, endX, endY);

                            // 4. 释放鼠标左键（结束拖动）
                            robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                        }
                        startDragResp = null; // 重置拖动记录
                    }

                } else if (operateType == 23) { // 23鼠标左键点击
                    if (targetX != null && targetY != null) {

                        //模拟移动
                        simulateMouseMove(robot, currentMousePos, targetX, targetY);

                    }
                    // 按下鼠标左键
                    robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                    // 释放鼠标左键
                    robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                } else if (operateType == 24) { // 24鼠标右键点击

                    if (targetX != null && targetY != null) {
                        //模拟移动
                        simulateMouseMove(robot, currentMousePos, targetX, targetY);
                    }
                    robot.delay(RandomNumberBetweenTwoParams.generateRandomNumber(0,2));
                    robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); // 按下鼠标右键
                    robot.delay(RandomNumberBetweenTwoParams.generateRandomNumber(0,2));
                    robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); // 释放鼠标右键

                } else if (operateType == 25) { // 25鼠标向上滚动
                    // 鼠标滚轮向上滚动
                    robot.mouseWheel(1); // 向上滚动 1 个单位
                } else if (operateType == 26) { // 26鼠标向下滚动
                    // 鼠标滚轮向下滚动
                    robot.mouseWheel(-1); // 向下滚动 1 个单位
                } else {
                    throw new CustomException("没有这个操作类型");
                }

                currentResp = findById(dataList, nextId);
            }

        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    /**
     * 模拟鼠标移动
     *
     * @param robot      用于控制鼠标操作的 Robot 对象
     * @param currentPos 鼠标当前的位置，使用 Point 对象表示，包含 x 和 y 坐标
     * @param targetX    鼠标要移动到的目标位置的 x 坐标
     * @param targetY    鼠标要移动到的目标位置的 y 坐标
     */
    private static void simulateMouseMove(Robot robot, Point currentPos, int targetX, int targetY) {
        if (currentPos == null) {
            return;
        }

        // 判断在 x 轴方向上鼠标是需要向右移动（正向）还是向左移动（负向）
        // 如果目标 x 坐标大于当前 x 坐标，dx 为 1，表示向右移动；否则为 -1，表示向左移动
        int dx = (targetX > currentPos.x) ? 1 : -1;
        // 判断在 y 轴方向上鼠标是需要向下移动（正向）还是向上移动（负向）
        // 如果目标 y 坐标大于当前 y 坐标，dy 为 1，表示向下移动；否则为 -1，表示向上移动
        int dy = (targetY > currentPos.y) ? 1 : -1;
        // 初始化当前 x 坐标为鼠标当前位置的 x 坐标
        int currentX = currentPos.x;
        // 初始化当前 y 坐标为鼠标当前位置的 y 坐标
        int currentY = currentPos.y;

        // 只要当前 x 坐标不等于目标 x 坐标或者当前 y 坐标不等于目标 y 坐标，就继续移动鼠标
        while (currentX != targetX || currentY != targetY) {
            // 如果当前 x 坐标不等于目标 x 坐标，根据 dx 的值更新当前 x 坐标
            // 若 dx 为 1 则 x 坐标加 1，若 dx 为 -1 则 x 坐标减 1
            if (currentX != targetX) {
                currentX += dx;
            }
            // 如果当前 y 坐标不等于目标 y 坐标，根据 dy 的值更新当前 y 坐标
            // 若 dy 为 1 则 y 坐标加 1，若 dy 为 -1 则 y 坐标减 1
            if (currentY != targetY) {
                currentY += dy;
            }
            // 使用 Robot 对象将鼠标移动到更新后的当前坐标位置
            robot.mouseMove(currentX, currentY);
            // 每移动一步后，暂停 N 毫秒，模拟人类操作鼠标时的自然速度和停顿
//            robot.delay(RandomNumberBetweenTwoParams.generateRandomNumber(0,2));
        }
    }

    /**
     * 根据id查询下一条记录
     *
     * @param list
     * @param id
     * @return
     */
    private static GameFunctionOperateFlowResp findById(List<GameFunctionOperateFlowResp> list, Long id) {
        for (GameFunctionOperateFlowResp resp : list) {
            if (Objects.equals(resp.getId(), id)) {
                return resp;
            }
        }
        return null;
    }
}
